/**
 * Copyright (C) 2012 LightAsyncHttpClient Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.oc.mba.http;

import java.io.UnsupportedEncodingException;
import java.util.WeakHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.SyncBasicHttpContext;

/**
 * <ul>
 * <li><b>name : </b>		LightAsyncHttp		</li>
 * <li><b>description :</b>	轻量级多线程异步HttpClient				</li>
 * <li><b>author : </b>		桥下一粒砂			</li>
 * <li><b>e-mail : </b>		chenyoca@gmail.com	</li>
 * <li><b>weibo : </b>		@桥下一粒砂			</li>
 * <li><b>date : </b>		2012-9-22 上午12:17:20		</li>
 * </ul>
 */
public class LightAsyncHttp {
    
    private static final int DEF_THREAD_POOL_SIZE = 2;
    
    private static final int MAX_THREAD_POOL_SIZE = 5;
    
    private static final int KEEP_ALIVE_TIME = 60;
    
    private final ThreadPoolExecutor mThreadPool;
    
	private final HttpContext mHttpContext;
	
	private final DefaultHttpClient mHttpClient;
	
	private final HttpClientSetting mHttpSettingBuilder;
	
	private final WeakHashMap<Integer, Future<?>> mThreadMaping;
	
	private Integer mThreadId = 0;
	
	private InvokeCallback mInvokeCallback;
	
	public interface InvokeCallback {
		void sendMessage(int requestId);
	}
	
	private static class SingletonContainer{        
        private static LightAsyncHttp instance = new LightAsyncHttp();        
    } 
	
	public static LightAsyncHttp getInstance(){        
        return SingletonContainer.instance;        
    }
	
	private LightAsyncHttp(){
		
		mHttpSettingBuilder  = new HttpClientSetting();
		
		final BasicHttpParams httpParams = mHttpSettingBuilder.buildDefaultHttpParams();
		SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 8443));
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
		
        mHttpContext = new SyncBasicHttpContext(new BasicHttpContext());
        mHttpClient = new DefaultHttpClient(cm, httpParams);
        
        final HttpRequestInterceptor reqInterceptor = mHttpSettingBuilder.getHttpRequestInterceptor(); 
        if( null != reqInterceptor){
        	mHttpClient.addRequestInterceptor(reqInterceptor);
        }
        final HttpResponseInterceptor respInterceptor = mHttpSettingBuilder.getHttpResponseInterceptor();
        if( null != respInterceptor){
        	mHttpClient.addResponseInterceptor(respInterceptor);
        }
        
        final HttpRequestRetryHandler retryHandler = mHttpSettingBuilder.getHttpRequestRetryHandler();
        if( null != retryHandler ){
        	mHttpClient.setHttpRequestRetryHandler(retryHandler);
        }
        
        mThreadMaping = new WeakHashMap<Integer, Future<?>>();
    	mInvokeCallback = new InvokeCallback(){
			@Override
			public void sendMessage(int requestId) {
				cancleRequest(requestId);
				mThreadMaping.remove(requestId);
			}
        };
        
        mThreadPool = new ThreadPoolExecutor(
				DEF_THREAD_POOL_SIZE, MAX_THREAD_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
				new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
        
	}
	
	/**
	 * 返回Http设置构建对象
	 * @return	Http设置构建对象
	 */
	public HttpClientSetting getHttpSetting(){
		return mHttpSettingBuilder;
	}
	
	/**
	 * 执行Get请求
	 * @param url		URL地址
	 * @param params	请求参数
	 * @param handler	回调接口
	 * @return			返回请求ID
	 */
	public int get(String url,RequestParams params,ResponseCallback handler){
		return invokeRequest(RequestParams.buildGetRequest(url, params), handler);
	}
	
	/**
	 * 执行POST请求
	 * @param url		URL地址
	 * @param params	请求参数
	 * @param handler	回调接口
	 * @return			返回请求ID
	 * @throws UnsupportedEncodingException 
	 */
	public int post(String url,RequestParams params,ResponseCallback handler) throws UnsupportedEncodingException{
		return invokeRequest(RequestParams.buildPostRequest(url, params), handler);
	}
	
	/**
	 * 指定字符编码执行POST请求
	 * @param url		URL地址
	 * @param params	请求参数
	 * @param handler	回调接口
	 * @return			返回请求ID
	 * @throws UnsupportedEncodingException 
	 */
	public int post(String url,RequestParams params,ResponseCallback handler,String encoding) throws UnsupportedEncodingException{
		return invokeRequest(RequestParams.buildPostRequest(url, params,encoding), handler);
	}
	
	/**
	 * 执行请求
	 * @param uriRequest	请求对象
	 * @param callback		回调接口
	 */
	public int invokeRequest( HttpUriRequest uriRequest,ResponseCallback callback){
		
		if( callback == null ) throw new IllegalArgumentException("ResponseCallback CANNOT be null");
		
		int threadId = 0;
		synchronized(mThreadId){
			threadId = mThreadId;
			mThreadId ++;
		}
		
		Future<?> thread = mThreadPool.submit( new RequestInvoker(mHttpClient, mHttpContext, uriRequest, callback, mInvokeCallback,threadId));
		synchronized(mThreadMaping){
			mThreadMaping.put(threadId, thread);
		}
		
		return threadId;
	}
	
	/**
	 * 取消一个请求
	 * @param requestId
	 */
	public void cancleRequest(int requestId){
		Future<?> thread = mThreadMaping.get(requestId);
		if( null != thread ){
			thread.cancel(true);
		}
	}
	
}
